/*
 * main.cpp
 *
 *  Created on: 30/05/2011
 *      Author: joaobosco
 */
#include <stdio.h>
#include <opencv.hpp>
#include <opencv2/core/core.hpp>
#include <dirent.h>
#include <string>
#include <vector>
#include "Imagem.hpp"
#include <time.h>
#include "Fisico.hpp"
#include <ctime>

using std::string;
using std::vector;

struct Triangulo{
	CvPoint pt1;
	CvPoint pt2;
	CvPoint pt3;
};

CvHaarClassifierCascade* cascade = 0;
CvMemStorage* storage = 0;

vector<string> listFiles(string dir, vector<string> extensions){

	vector<string> files;

	DIR *directory;
	dirent *dir_file;

	if((directory  = opendir(dir.c_str())) == NULL) {
		//		printf("BioPassUtils: Error '%d' while trying to open the directory %s", errno, dir.c_str());
		throw "BioPassUtils: Error '%d' while trying to open the directory %s";
	}

	while ((dir_file = readdir(directory)) != NULL) {

		string name = dir_file->d_name;

		unsigned int ext_pos = name.rfind(".");

		if (ext_pos != string::npos) {

			string ext = name.substr(ext_pos+1,string::npos);

			for (unsigned int i = 0; i < extensions.size(); i++) {
				string extension = extensions.at(i);

				if (stricmp(ext.c_str(),extension.c_str()) == 0) {
					files.push_back(dir_file->d_name);
				}
			}

		}
	}
	closedir(directory);

	return files;

}

void capturaContinuo(){
	CvCapture *capture = 0;
	IplImage  *frame = 0;
	int       key = 0;

	/* initialize camera */
	capture = cvCaptureFromCAM(0);

	/* always check */
	if ( !capture ) {
		fprintf( stderr, "Cannot open initialize webcam!\n" );
	}

	frame = cvQueryFrame( capture );

	Imagem* aux;

	int* vetor;

	cvNamedWindow( "result", CV_WINDOW_AUTOSIZE );
	while( key != 'q' ) {
		/* display current frame */
		frame = cvQueryFrame( capture );

		if( !frame ) break;

		cvShowImage( "result", frame );

		if(key == 'c'){
			aux = new Imagem(frame);

			vetor = Imagem::mediaQuadranteCentral(aux);
			printf("Media B G R = %d; %d; %d;\n", vetor[0], vetor[1], vetor[2]);

			vetor = Imagem::medianQuadranteCentral(aux);
			printf("Mediana B G R = %d; %d; %d;\n", vetor[0], vetor[1], vetor[2]);
		}

		/* exit if user press 'q' */
		key = cvWaitKey( 1 );
	}
}

void criarJanela(const char* name){
	cvNamedWindow( name, CV_WINDOW_AUTOSIZE );

	cvMoveWindow(name, 0, 0);
}

double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 ) {
	double dx1 = pt1->x - pt0->x;
	double dy1 = pt1->y - pt0->y;
	double dx2 = pt2->x - pt0->x;
	double dy2 = pt2->y - pt0->y;
	return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}

double anglev( CvPoint* pt1, CvPoint* pt2, CvPoint* pt3, CvPoint* pt4) {

	double dx1 = pt2->x - pt1->x;
	double dy1 = pt2->y - pt1->y;
	double dx2 = pt4->x - pt3->x;
	double dy2 = pt4->y - pt3->y;
	double dot = dx1 * dx2 + dy1 * dy2;
	double n1 = sqrt(dx1 * dx1 + dy1 * dy1 + 1e-10);
	double n2 = sqrt(dx2 * dx2 + dy2 * dy2 + 1e-10);

	return dot / (n1 * n2);
}

// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
CvSeq* findSquares( IplImage* img, CvMemStorage* storage ) {

	CvSeq* contours = 0;
	int i, c, l, N = 11;
	CvSize sz = cvSize( img->width & -2, img->height & -2 );
	IplImage* timg = cvCloneImage( img ); // make a copy of input image
	IplImage* gray = cvCreateImage( sz, 8, 1 );
	IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 );
	IplImage* tgray;
	CvSeq* result = 0;
	double s, t;
	int thresh = 50;
	// create empty sequence that will contain points -
	// 4 points per square (the square's vertices)
	CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );


	// select the maximum ROI in the image
	// with the width and height divisible by 2
	cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height));

	// down-scale and upscale the image to filter out the noise
	cvPyrDown( timg, pyr, 7 );
	cvPyrUp( pyr, timg, 7 );
	tgray = cvCreateImage( sz, 8, 1 );

	// find squares in every color plane of the image
	for( c = 2; c < 3; c++ )
	{
		// extract the c-th color plane
		cvSetImageCOI( timg, c+1 );
		cvCopy( timg, tgray, 0 );

		//cvShowImage("Teste", timg);

		// try several threshold levels
		for( l = 0; l < N; l++ )
		{
			// hack: use Canny instead of zero threshold level.
			// Canny helps to catch squares with gradient shading
			if( l == 0 )
			{
				// apply Canny. Take the upper threshold from slider
				// and set the lower to 0 (which forces edges merging)
				cvCanny( tgray, gray, 0, thresh, 5 );
				// dilate canny output to remove potential
				// holes between edge segments
				cvDilate( gray, gray, 0, 1 );
			}
			else
			{
				// apply threshold if l!=0:
				//     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
				cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY );
			}

			// find contours and store them all as a list
			CvMemStorage* storage = cvCreateMemStorage(0);
			cvFindContours( gray, storage, &contours, sizeof(CvContour),
					CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );

			//cvReleaseMemStorage(&storage);
			// test each contour
			CvMemStorage* storage2 = cvCreateMemStorage(0);
			CvSeq* aux = contours;
			while( aux )
			{
				// approximate contour with accuracy proportional
				// to the contour perimeter
				result = cvApproxPoly( aux, sizeof(CvContour), storage2,
						CV_POLY_APPROX_DP, cvContourPerimeter(aux)*0.02, 0 );
				// square contours should have 4 vertices after approximation
				// relatively large area (to filter out noisy contours)
				// and be convex.
				// Note: absolute value of an area is used because
				// area may be positive or negative - in accordance with the
				// contour orientation
				if( result->total == 3 &&
						cvContourArea(result,CV_WHOLE_SEQ,0) > 1000 &&
						cvCheckContourConvexity(result) )
				{
					s = 0;
					for( i = 0; i < 3; i++ )
					{
						// find minimum angle between joint
						// edges (maximum of cosine)
						if( i >= 2 )
						{
							t = fabs(angle(
									(CvPoint*)cvGetSeqElem( result, i ),
									(CvPoint*)cvGetSeqElem( result, i-2 ),
									(CvPoint*)cvGetSeqElem( result, i-1 )));
							s = s > t ? s : t;
						}
					}

					// if cosines of all angles are small
					// (all angles are ~90 degree) then write quandrange
					// vertices to resultant sequence
					if( s < 0.55 && s > 0.45 )
						for( i = 0; i < 3; i++ )
							cvSeqPush( squares,
									(CvPoint*)cvGetSeqElem( result, i ));
				}

				// take the next contour
				aux = aux->h_next;
			}

			if (contours) {

				cvClearSeq(contours);
				contours = 0;
				cvReleaseMemStorage(&storage);
			}

			if (result) {

				cvClearSeq(result);
				result = 0;
				cvReleaseMemStorage(&storage2);
			}
		}
	}

	// release all the temporary images
	cvReleaseImage( &gray );
	cvReleaseImage( &pyr );
	cvReleaseImage( &tgray );
	cvReleaseImage( &timg );

	return squares;
}

// the function draws all the squares in the image
void drawSquares( IplImage* img, CvSeq* squares ) {

	CvSeqReader reader;
	bool first = true;
	CvPoint* fp = 0;
	IplImage* cpy = img;//cvCloneImage( img );
	int i;

	// initialize reader of the sequence
	cvStartReadSeq( squares, &reader, 0 );

	// read 3 sequence elements at a time (all vertices of a square)
	for( i = 0; i < squares->total; i += 3 )
	{
		CvPoint pt[3], *rect = pt;
		int count = 3;

		// read 4 vertices
		CV_READ_SEQ_ELEM( pt[0], reader );
		CV_READ_SEQ_ELEM( pt[1], reader );
		CV_READ_SEQ_ELEM( pt[2], reader );

		if (first) {

			fp = new CvPoint[3];
			fp[0].x = pt[0].x;
			fp[0].y = pt[0].y;
			fp[1].x = pt[1].x;
			fp[1].y = pt[1].y;
			fp[2].x = pt[2].x;
			fp[2].y = pt[2].y;
			first = false;
		}

		// draw the square as a closed polyline
		cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,0,0), 3, CV_AA, 0);
	}


	//return fp;
}

CvPoint* calculaBaricentro(CvPoint* pts){
	CvPoint *baricentro = new CvPoint();

	baricentro->x = (int)(pts[0].x + pts[1].x + pts[2].x)/3;
	baricentro->y = (int)(pts[0].y + pts[1].y + pts[2].y)/3;

	return baricentro;
}

CvPoint* cvSeq2cvPoint(CvSeq* ptsSeq){
	CvSeqReader reader;
	CvPoint *ptsPoint = new CvPoint[ptsSeq->total];
	int i, j;

	/* Passa os pontos para o leitor. */
	cvStartReadSeq( ptsSeq, &reader, 0 );

	for(i = 0; i < ptsSeq->total; i++){
		CvPoint pt;

		CV_READ_SEQ_ELEM( pt, reader );

		ptsPoint[i].x = pt.x;
		ptsPoint[i].y = pt.y;
	}
	return ptsPoint;
}

CvPoint** calculaNBaricentros(CvSeq* pontos){
	CvPoint **baricentros = new CvPoint*[pontos->total/3];
	CvSeqReader reader;
	int i, j;

	/* Passa os pontos para o leitor. */
	cvStartReadSeq( pontos, &reader, 0 );

	// read 3 sequence elements at a time (all vertices of a square)
	for( i = 0, j = 0; i < pontos->total; i += 3, j++ )
	{
		CvPoint pt[3];

		// read 4 vertices
		CV_READ_SEQ_ELEM( pt[0], reader );
		CV_READ_SEQ_ELEM( pt[1], reader );
		CV_READ_SEQ_ELEM( pt[2], reader );

		baricentros[j] = calculaBaricentro(pt);

		printf("Baricentro[%d]: x = %d; y = %d;\n", j, baricentros[j]->x, baricentros[j]->y);
	}

	return baricentros;
}

void eliminaTriangulosRepetidos(CvSeq* pontos){
	CvSeqReader reader;
	CvPoint* pts, **baricentros;
	int i, j;

	baricentros = calculaNBaricentros(pontos);

	pts = cvSeq2cvPoint(pontos);

}



//int main(int argc, char **argv) {
//
//	int key = 0;
//
//	CvCapture *capture = 0;
//	IplImage  *frame = 0;
//
//	IplImage* img;
//	Imagem *nova, *dup;
//	img=cvLoadImage("C:\\Users\\Berg\\Desktop\\CamadaFisica\\EclipseHeliosCDT\\branco.png");
//
//	capture = cvCaptureFromCAM(0);
//
//	criarJanela("result");
//
//	while(key != 'q'){
//		frame = cvQueryFrame( capture );
//
//		if(key == 'c'){
//			nova = new Imagem(frame);
//			dup = nova->duplicate();
//			nova->binario();
//			if(nova == 0 || nova->getIplImage() == 0){
//				printf("Nenhuma imagem foi capturada nesse cabareh!\n");
//			}
//		}
//		cvShowImage( "result", frame);
//		key = cvWaitKey( 1 );
//
//	}
//	cvDestroyWindow( "result" );
//	cvReleaseCapture( &capture );
//	key = 0;
//	//int c1 = clock();
//	time_t t1, t2;
//	time(&t1);
//	dup->binario();
//	Imagem::analizaStart(dup);
//	//Bits* numSei = Fisico::identificaBitssss(dup);
//	time(&t2);
//	//int c2 = clock();
//	printf("CLOCK = %d\n", t2 - t1);
//
//
////	for(int i = 0; i < numSei->length(); i++){
////		printf("Bit[%d] = %d\n", i, numSei->get(i));
////	}
////	criarJanela("result");
////
////	while(key != 'q'){
////		cvShowImage( "result", dup->getIplImage());
////		key = cvWaitKey( 1 );
////	}
//}
